clojure transducers

这个也是前两天看的,当时让两个llm给我解释transducers,然后我一个字都看不懂,急得我满头大汗……这时候我去看了下clojure原文档,发现还是原文档写得明白。

首先要知道什么是reducer。reducer就是类似于 sum acc xs = 把列表xs里的内容统统加到acc上,返回acc 这样的函数。
还有如 filter pred xs = 看看xs列表里的内容符不符合pred(一个predicate),符合的话放进新列表,不符合的踢掉,最后返回新列表
还有经典的map f xs = 把f apply到xs里所有元素,返回新列表
感觉还是画个图比较直观:
reducer
x
xs
一个列表
操作完了返回这个
x
全都操作一遍

(我一直都觉得,在解释函数式编程的时候使用linear notation,是比较反人类的。)
这里我本应该说:reducer属于xxx category,之类的话,但我不知道xxx应该是什么。可能是kleisli category吧。

那么,有的时候我们会想要把多个reducers套在一起。例如

在这个过程中,map会产生一个新list,filter又会产生一个新list,因为这些都不是in place操作。

有人就说,不能做一个新reducer map_inc_1_filter_even_sum 吗,这样我们只需要对每个元素做以下操作:

这样最后会是一样的结果。

那么transducer就是clojure搞出来的一个“把reducer打包在一起的工具”。
字面意思就是reducer transformer。
我们看一下文档:

(def xf
  (comp
    (filter odd?)
    (map inc)
    (take 5)))

xf是一个transducer(x for trans),还需要传入一个reducer,才会得到一个reducer。
然后以xf rf 参数这样的形式去使用,rf是最后一个reducer,会最后一个执行。

这里的关键是,在clojure里,像(filter odd?)这样只有单个参数的reducer,其实是一个transducer,而不是等待curry的reducer!
而transducer的signature是:

transducer :: reducer -> reducer

我偷懒了,把reducer当成一个type写出来。
总之transducer会拿到一个reducer作为参数,变形一下,然后返回一个新reducer。

所以,当xf rf的时候:

这里我也想画图,但没有手绘板,还是算了。
在学数学的时候,有一个白板真的很重要。